package fr.cdesire.sudoku.core.commons;

import java.util.EnumSet;
import java.util.Set;

import fr.cdesire.sudoku.core.enums.Valeur;

/**
 * Représente une case de grille Sudoku. Elle associe une position de la grille à une
 * valeur (case pleine) ou plusieurs possibilités de valeurs (case vide).
 * @author cedric.desire@gmail.com
 */
public class Case implements Comparable<Case> {
    
    /**
     * Position de la case.
     */
    private final Position position;

    /**
     * Liste des valeurs possibles dans la case.
     */
    private final Set<Valeur> valeurs;

    /**
     * Constructeur privé : c'est la création d'une grille qui construit l'ensemble des 
     * cases dont elle est constituée.
     * @param position Position de la case dans la grille.
     * @param valeurs Liste des valeurs possibles de la case.
     */
    protected Case(final Position position, final Set<Valeur> valeurs) {
    	
    	this.position = position;
    	this.valeurs = ((EnumSet<Valeur>)valeurs).clone();
    }

    /**
     * Retourne la position de la case.
     * @return Position de la case.
     */
    public Position getPosition() {
        return position;
    }

    /**
     * Retourne la liste des valeurs possible de la case.
     * @return Liste des valeurs possibles de la case.
     */
    public Set<Valeur> getValeurs() {
        return valeurs;
    }

    /**
     * Retourne la valeur de la case s'il n'existe qu'une possibilité, null sinon.
     * @return Valeur de la case ou null s'il existe plusieurs possibilités.
     */
    public Valeur getValeur() {
        if (!valeurs.isEmpty() && valeurs.size() == 1) {
            return valeurs.iterator().next();
        }
        return null;
    }

    /**
     * Indique si la case est renseignée ou vide.
     * @return Vrai ou faux.
     */
    public boolean isCaseVide() {
        return getValeur() == null;
    }

    /**
     * Redéfinit l'égalité entre 2 cases en fonction de leur position et de leurs possibilités éventuelles.
     * @param obj Case.
     * @return Vrai ou faux.
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Case other = (Case) obj;
        if (this.position != other.position && (this.position == null || !this.position.equals(other.position))) {
            return false;
        }
        if (this.valeurs != other.valeurs && (this.valeurs == null || !this.valeurs.equals(other.valeurs))) {
            return false;
        }
        return true;
    }

    /**
     * Redéfinit le hashcode d'une case en fonction de sa position et de ses possibilités éventuelles.
     * @return Hashcode.
     */
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 29 * hash + this.position.hashCode();
        hash = 29 * hash + this.valeurs.hashCode();
        return hash;
    }
	
    /**
     * Redéfinit la comparaison entre 2 cases en fonction du nombre de possibilités et de sa position.
     * @param obj Case à comparer.
     * @return -1, 0 1 si inférieur, égal ou supérieur.
     */
    @Override
    public int compareTo(Case obj) {
        int compare = ( new Integer(this.valeurs.size()) ).compareTo(new Integer(obj.getValeurs().size()));
        if (compare == 0) {
            compare = ( new Integer(this.position.hashCode()) ).compareTo(new Integer(obj.getPosition().hashCode()));
        }
        return compare;
    }
}